#pragma once

#include "Defines.h"

// common include
#include <windows.h>
#include <wrl.h>
#include <vector>

// include d2d
#include <d2d1.h>
#include <d2d1_1.h>
#include <d2d1_1helper.h>
#include <d2d1_2.h>
#include <d2d1_3.h>
#include <d2d1_3helper.h>
#include <d2d1effectauthor.h>
#include <d2d1effectauthor_1.h>
#include <d2d1effecthelpers.h>
#include <d2d1effects.h>
#include <d2d1effects_1.h>
#include <d2d1effects_2.h>
#include <d2d1helper.h>
#include <d2d1svg.h>
#include <dcommon.h>

#pragma comment(lib, "d2d1")

// include d3d 
#include <d3d11.h>
#include <d3d11_1.h>
#include <d3d11_2.h>
#include <d3d11_3.h>
#include <d3d11_4.h>
#include <d3d11sdklayers.h>
#include <d3d11shader.h>
#include <d3d11shadertracing.h>
#include <d3dcommon.h>
#include <d3dcsx.h>

#pragma comment(lib, "d3d11")

// 引入 dxgi 接口
#include <dxgi.h>
#include <dxgi1_2.h>
#include <dxgi1_3.h>
#include <dxgi1_4.h>
#include <dxgi1_5.h>
#include <dxgi1_6.h>
#include <dxgicommon.h>
#include <dxgidebug.h>
#include <dxgiformat.h>

#pragma comment(lib, "dxgi")

#include <dwrite.h>
#pragma comment(lib, "dwrite")

namespace WinDirectUI
{
	using Microsoft::WRL::ComPtr;

#define FeatureLevelSize 7
	/// <summary>
	/// Windows 设备资源资源
	/// </summary>
	class DeviceResources
	{
		PROTECTED DeviceResources() 
			: m_d2dFactory(nullptr),
			m_d2dDevice(nullptr),
			m_d3d11Device(nullptr)
		{
			Reset();
		}

		PROTECTED void Reset()
		{
			m_d2dFactory = nullptr;
			m_d2dDevice = nullptr;
			m_d3d11Device = nullptr;

			this->CreateD3DResources();
			this->CreateD2DResources();
		}

		// Direct2D objects.
		PRIVATE ComPtr<ID2D1Factory2> m_d2dFactory;
		PRIVATE ComPtr<ID2D1Device1> m_d2dDevice;

		// Direct3D objects
		PRIVATE ComPtr<ID3D11Device> m_d3d11Device;
		PRIVATE ComPtr<ID3D11DeviceContext> m_d3d11DeviceContext;

		PROTECTED ID2D1Factory2* GetD2DFactory() { return m_d2dFactory.Get(); }
		PROTECTED ID2D1Device1* GetD2DDevice(){ return m_d2dDevice.Get(); }

		PROTECTED ID3D11Device* GetD3D11Device() { return m_d3d11Device.Get(); }
		PROTECTED ID3D11DeviceContext* GetD3D11DeviceContext() { return m_d3d11DeviceContext.Get(); }

		/// <summary>
		/// 初始化 D3D 设备
		/// </summary>
		PRIVATE void CreateD3DResources()
		{
			D3D_FEATURE_LEVEL featureLevels[FeatureLevelSize] =
			{
				D3D_FEATURE_LEVEL_11_1,
				D3D_FEATURE_LEVEL_11_0,
				D3D_FEATURE_LEVEL_10_1,
				D3D_FEATURE_LEVEL_10_0,
				D3D_FEATURE_LEVEL_9_3,
				D3D_FEATURE_LEVEL_9_2,
				D3D_FEATURE_LEVEL_9_1
			};

			D3D_FEATURE_LEVEL featureLevelSupported;

			// https://learn.microsoft.com/zh-cn/windows/win32/api/d3d11/nf-d3d11-d3d11createdevice
			// 如果 DriverType == D3D_DRIVER_TYPE_HARDWARE，则使用的适配器将是默认适配器，
			// 这是 IDXGIFactory1：：EnumAdapters 枚举的第一个适配器
			// 如果将 pAdapter 参数设置为非 NULL 值，将 DriverType 参数设置为D3D_DRIVER_TYPE_HARDWARE值，此方法将返回E_INVALIDARG。
			// 所以 pAdapter 这里必须设置为 nullptr

			// 如果计算机上存在 Direct3D 11.1 运行时，并且 pFeatureLevels 设置为 NULL，则此函数不会创建 D3D_FEATURE_LEVEL_11_1 设备。 
			// 若要创建D3D_FEATURE_LEVEL_11_1设备，必须显式提供包含D3D_FEATURE_LEVEL_11_1的D3D_FEATURE_LEVEL数组。
			// 如果在未安装 Direct3D 11.1 运行时的计算机上提供包含D3D_FEATURE_LEVEL_11_1的D3D_FEATURE_LEVEL数组，
			// 则此函数将立即失败并E_INVALIDARG。
			ThrowIfFailed(
				::D3D11CreateDevice(
					nullptr, // pAdapter
					D3D_DRIVER_TYPE::D3D_DRIVER_TYPE_HARDWARE, // DriverType
					nullptr, // Software
					// D3D11_CREATE_DEVICE_BGRA_SUPPORT 创建支持 BGRA 格式 (DXGI_FORMAT_B8G8R8A8_UNORM 
					// 和 DXGI_FORMAT_B8G8R8A8_UNORM_SRGB) 的设备
					// D3D 设备必须指定 D3D11_CREATE_DEVICE_BGRA_SUPPORT，否则不能创建 D2D 设备
					D3D11_CREATE_DEVICE_FLAG::D3D11_CREATE_DEVICE_BGRA_SUPPORT
					| D3D11_CREATE_DEVICE_FLAG::D3D11_CREATE_DEVICE_SINGLETHREADED, // Flags
					featureLevels, // pFeatureLevels
					FeatureLevelSize, // FeatureLevels
					D3D11_SDK_VERSION, // SDKVersion
					m_d3d11Device.GetAddressOf(), // ppDevice
					&featureLevelSupported, // pFeatureLevel
					m_d3d11DeviceContext.GetAddressOf()) // ppImmediateContext
			);

			if (D3D_FEATURE_LEVEL_11_1 != featureLevelSupported)
			{
				throw new InvalidOperationException("当前 windows 不支持 D3D_FEATURE_LEVEL_11_1.");
			}
		}

		/// <summary>
		/// 初始化 D2D 设备资源
		/// </summary>
		PRIVATE void CreateD2DResources()
		{
			ThrowIfFailed(
				::D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_PPV_ARGS(m_d2dFactory.GetAddressOf()))
			);

			ComPtr<IDXGIDevice> dxgiDevice;

			ThrowIfFailed(
				this->m_d3d11Device->QueryInterface(dxgiDevice.GetAddressOf())
			);

			ThrowIfFailed(
				// D3D 设备必须指定 D3D11_CREATE_DEVICE_BGRA_SUPPORT，否则不能创建 D2D 设备
				this->m_d2dFactory->CreateDevice(dxgiDevice.Get(), m_d2dDevice.GetAddressOf())
			);
		}
	};
}